// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package syscall

import (
	"unsafe"
)

const (
	STANDARD_RIGHTS_REQUIRED = 0xf0000
	STANDARD_RIGHTS_READ     = 0x20000
	STANDARD_RIGHTS_WRITE    = 0x20000
	STANDARD_RIGHTS_EXECUTE  = 0x20000
	STANDARD_RIGHTS_ALL      = 0x1F0000
)

const (
	NameUnknown          = 0
	NameFullyQualifiedDN = 1
	NameSamCompatible    = 2
	NameDisplay          = 3
	NameUniqueId         = 6
	NameCanonical        = 7
	NameUserPrincipal    = 8
	NameCanonicalEx      = 9
	NameServicePrincipal = 10
	NameDnsDomain        = 12
)

// 此函数返回1字节的布尔值，而不是4字节的布尔值。
// https:
// sys TranslateName（accName*uint16，accNameFormat uint32，desiredNameFormat uint32，translatedName*uint16，nSize*uint32）（错误）[failretval&0xff==0]=secur32.TranslateName新
// sys GetUserName（nameFormat uint32，namebuffer*uint16，nSize*uint32）（错误）[failretval&0xff==0]=secur32.GetUserNameExW 

// TranslateAccountName将目录服务
// 对象名从一种格式转换为另一种格式。
func TranslateAccountName(username string, from, to uint32, initSize int) (string, error) {
	u, e := UTF16PtrFromString(username)
	if e != nil {
		return "", e
	}
	n := uint32(50)
	for {
		b := make([]uint16, n)
		e = TranslateName(u, from, to, &b[0], &n)
		if e == nil {
			return UTF16ToString(b[:n]), nil
		}
		if e != ERROR_INSUFFICIENT_BUFFER {
			return "", e
		}
		if n <= uint32(len(b)) {
			return "", e
		}
	}
}

const (
	// 不要重新排序
	NetSetupUnknownStatus = iota
	NetSetupUnjoined
	NetSetupWorkgroupName
	NetSetupDomainName
)

type UserInfo10 struct {
	Name       *uint16
	Comment    *uint16
	UsrComment *uint16
	FullName   *uint16
}

// sys NetUserGetInfo（serverName*uint16，userName*uint16，level uint32，buf**byte）（neterror）=netapi32.NetUserGetInfo 
// sys NetGetJoinInformation（server*uint16，name**uint16，bufType*uint32）（neterror）=netapi32.NetGetJoinInformation 

const (
	// 请勿重新排序
	SidTypeUser = 1 + iota
	SidTypeGroup
	SidTypeDomain
	SidTypeAlias
	SidTypeWellKnownGroup
	SidTypeDeletedAccount
	SidTypeInvalid
	SidTypeUnknown
	SidTypeComputer
	SidTypeLabel
)

// sys LookupAccountSid（systemName*uint16，sid*sid，name*uint16，nameLen*uint32，refdDomainName*uint16，refdDomainNameLen*uint32，use*uint32）（错误）=advapi32.LookupAccountSidW 
// sys ConvertSidToStringSid**uint16）（错误）=advapi32.convertsidtostringstringsidtosidw 
// sys ConvertStringSidToSid（stringSid*uint16，sid**sid）（错误）=advapi32.ConvertStringSidToSidW 
// sys GetLengthSid（sid*sid）（len uint32）=advapi32.GetLengthSid 
// sys CopySid（destSid uint32，destSid*sid，srcSid*sid）（错误）=advapi32.CopySid 

// 结构，用于唯一标识用户或组。
type SID struct{}

// StringToSid将字符串格式的安全标识符
// sid转换为有效的功能sid。
func StringToSid(s string) (*SID, error) {
	var sid *SID
	p, e := UTF16PtrFromString(s)
	if e != nil {
		return nil, e
	}
	e = ConvertStringSidToSid(p, &sid)
	if e != nil {
		return nil, e
	}
	defer LocalFree((Handle)(unsafe.Pointer(sid)))
	return sid.Copy()
}

// LookupSID检索帐户
// 和nam的安全标识符sid找到帐户的域的e。
// 系统指定要搜索的目标计算机。
func LookupSID(system, account string) (sid *SID, domain string, accType uint32, err error) {
	if len(account) == 0 {
		return nil, "", 0, EINVAL
	}
	acc, e := UTF16PtrFromString(account)
	if e != nil {
		return nil, "", 0, e
	}
	var sys *uint16
	if len(system) > 0 {
		sys, e = UTF16PtrFromString(system)
		if e != nil {
			return nil, "", 0, e
		}
	}
	n := uint32(50)
	dn := uint32(50)
	for {
		b := make([]byte, n)
		db := make([]uint16, dn)
		sid = (*SID)(unsafe.Pointer(&b[0]))
		e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType)
		if e == nil {
			return sid, UTF16ToString(db), accType, nil
		}
		if e != ERROR_INSUFFICIENT_BUFFER {
			return nil, "", 0, e
		}
		if n <= uint32(len(b)) {
			return nil, "", 0, e
		}
	}
}

// 字符串将sid转换为字符串格式
// 适合显示、存储或传输。
func (sid *SID) String() (string, error) {
	var s *uint16
	e := ConvertSidToStringSid(sid, &s)
	if e != nil {
		return "", e
	}
	defer LocalFree((Handle)(unsafe.Pointer(s)))
	return utf16PtrToString(s), nil
}

// Len返回有效安全标识符sid的长度（字节）。
func (sid *SID) Len() int {
	return int(GetLengthSid(sid))
}

// Copy创建安全标识符sid的副本。
func (sid *SID) Copy() (*SID, error) {
	b := make([]byte, sid.Len())
	sid2 := (*SID)(unsafe.Pointer(&b[0]))
	e := CopySid(uint32(len(b)), sid2, sid)
	if e != nil {
		return nil, e
	}
	return sid2, nil
}

// LookupAccount检索此sid的帐户名
// 以及找到此sid的第一个域的名称。
// 系统指定要搜索的目标计算机。
func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) {
	var sys *uint16
	if len(system) > 0 {
		sys, err = UTF16PtrFromString(system)
		if err != nil {
			return "", "", 0, err
		}
	}
	n := uint32(50)
	dn := uint32(50)
	for {
		b := make([]uint16, n)
		db := make([]uint16, dn)
		e := LookupAccountSid(sys, sid, &b[0], &n, &db[0], &dn, &accType)
		if e == nil {
			return UTF16ToString(b), UTF16ToString(db), accType, nil
		}
		if e != ERROR_INSUFFICIENT_BUFFER {
			return "", "", 0, e
		}
		if n <= uint32(len(b)) {
			return "", "", 0, e
		}
	}
}

const (
	// 不重新排序
	TOKEN_ASSIGN_PRIMARY = 1 << iota
	TOKEN_DUPLICATE
	TOKEN_IMPERSONATE
	TOKEN_QUERY
	TOKEN_QUERY_SOURCE
	TOKEN_ADJUST_PRIVILEGES
	TOKEN_ADJUST_GROUPS
	TOKEN_ADJUST_DEFAULT
	TOKEN_ADJUST_SESSIONID

	TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
		TOKEN_ASSIGN_PRIMARY |
		TOKEN_DUPLICATE |
		TOKEN_IMPERSONATE |
		TOKEN_QUERY |
		TOKEN_QUERY_SOURCE |
		TOKEN_ADJUST_PRIVILEGES |
		TOKEN_ADJUST_GROUPS |
		TOKEN_ADJUST_DEFAULT |
		TOKEN_ADJUST_SESSIONID
	TOKEN_READ  = STANDARD_RIGHTS_READ | TOKEN_QUERY
	TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
		TOKEN_ADJUST_PRIVILEGES |
		TOKEN_ADJUST_GROUPS |
		TOKEN_ADJUST_DEFAULT
	TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE
)

const (
	// 不重新排序der 
	TokenUser = 1 + iota
	TokenGroups
	TokenPrivileges
	TokenOwner
	TokenPrimaryGroup
	TokenDefaultDacl
	TokenSource
	TokenType
	TokenImpersonationLevel
	TokenStatistics
	TokenRestrictedSids
	TokenSessionId
	TokenGroupsAndPrivileges
	TokenSessionReference
	TokenSandBoxInert
	TokenAuditPolicy
	TokenOrigin
	TokenElevationType
	TokenLinkedToken
	TokenElevation
	TokenHasRestrictions
	TokenAccessInformation
	TokenVirtualizationAllowed
	TokenVirtualizationEnabled
	TokenIntegrityLevel
	TokenUIAccess
	TokenMandatoryPolicy
	TokenLogonSid
	MaxTokenInfoClass
)

type SIDAndAttributes struct {
	Sid        *SID
	Attributes uint32
}

type Tokenuser struct {
	User SIDAndAttributes
}

type Tokenprimarygroup struct {
	PrimaryGroup *SID
}

// sys OpenProcessToken（h句柄，访问uint32，token*token）（错误）=advapi32.OpenProcessToken 
// sys GetTokenInformation（t令牌，infoClass uint32，info*字节，infoLen uint32，returnedLen*uint32）（错误）=advapi32.GetTokenInformation 
// /sys-getSystemDirectory（dir*uint16，dirLen-uint32）（len-uint32，错误）=kernel32.GetSystemDirectoryW 

// 访问令牌包含登录会话的安全信息。
// 当用户登录时，系统创建一个访问令牌，并且代表用户执行的每个
// 进程都有一个令牌的副本。
// 该令牌标识用户，即ser的组和用户的
// 权限。系统使用令牌控制对安全
// 对象的访问，并控制用户在本地计算机上执行各种
// 系统相关操作的能力。
type Token Handle

// OpenCurrentProcessToken打开访问令牌
// 与当前进程关联。
func OpenCurrentProcessToken() (Token, error) {
	p, e := GetCurrentProcess()
	if e != nil {
		return 0, e
	}
	var t Token
	e = OpenProcessToken(p, TOKEN_QUERY, &t)
	if e != nil {
		return 0, e
	}
	return t, nil
}

// Close释放对访问令牌的访问。
func (t Token) Close() error {
	return CloseHandle(Handle(t))
}

// getInfo检索有关访问令牌的指定类型的信息。
func (t Token) getInfo(class uint32, initSize int) (unsafe.Pointer, error) {
	n := uint32(initSize)
	for {
		b := make([]byte, n)
		e := GetTokenInformation(t, class, &b[0], uint32(len(b)), &n)
		if e == nil {
			return unsafe.Pointer(&b[0]), nil
		}
		if e != ERROR_INSUFFICIENT_BUFFER {
			return nil, e
		}
		if n <= uint32(len(b)) {
			return nil, e
		}
	}
}

// GetTokenUser检索访问令牌t用户帐户信息。
func (t Token) GetTokenUser() (*Tokenuser, error) {
	i, e := t.getInfo(TokenUser, 50)
	if e != nil {
		return nil, e
	}
	return (*Tokenuser)(i), nil
}

// GetTokenPrimaryGroup检索访问令牌t primary group information.
// 指向SID结构的指针，表示将成为
// 由使用此访问令牌的进程创建的任何对象的主组的组。
func (t Token) GetTokenPrimaryGroup() (*Tokenprimarygroup, error) {
	i, e := t.getInfo(TokenPrimaryGroup, 50)
	if e != nil {
		return nil, e
	}
	return (*Tokenprimarygroup)(i), nil
}

// GetUserProfileDirectory检索访问令牌t用户配置文件的
// 根目录的路径。
func (t Token) GetUserProfileDirectory() (string, error) {
	n := uint32(100)
	for {
		b := make([]uint16, n)
		e := GetUserProfileDirectory(t, &b[0], &n)
		if e == nil {
			return UTF16ToString(b), nil
		}
		if e != ERROR_INSUFFICIENT_BUFFER {
			return "", e
		}
		if n <= uint32(len(b)) {
			return "", e
		}
	}
}
